<template>
  <div class="tree-search-container">
    <!-- 搜索输入框 -->

    <el-input
      v-model="searchText"
      placeholder="输入关键词搜索"
      prefix-icon="Search"
      class="search-input"
      @input="handleSearch"
    />
    <!-- 树形组件 -->
    <el-tree
      ref="treeRef"
      :data="filteredData"
      :props="treeProps"
      :default-expand-all="false"
      :expand-on-click-node="false"
      :default-expanded-keys="expandedKeys"
      node-key="id"
      class="custom-tree"
    >
      <!-- 自定义节点内容，高亮显示匹配的文本 -->
      <template #default="{ node }">
        <span v-html="highlightMatch(node.label)"></span>
      </template>
    </el-tree>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'
import { ElTree, ElInput } from 'element-plus'

// 树形结构配置
const treeProps = {
  label: 'label',
  children: 'children',
  id: 'id'
}

// 模拟树形数据
const originalData = ref([
  {
    id: 1,
    label: '一级节点 A',
    children: [
      {
        id: 11,
        label: '二级节点 A-1',
        children: [
          { id: 111, label: '三级节点 A-2-1' },
          { id: 112, label: '三级节点 A-2-2' }
        ]
      },
      { id: 12, label: '二级节点 A-2' }
    ]
  },
  {
    id: 2,
    label: '一级节点 B',
    children: [
      { id: 21, label: '二级节点 B-1' },
      {
        id: 22,
        label: '二级节点 B-2',
        children: [
          { id: 221, label: '三级节点 B-3-1' },
          { id: 222, label: '三级节点 B-3-2' }
        ]
      }
    ]
  },
  {
    id: 3,
    label: '一级节点 B',
    children: [
      { id: 31, label: '二级节点 B-1' },
      {
        id: 32,
        label: '二级节点 B-2',
        children: [
          { id: 331, label: '三级节点 B-3-1' },
          { id: 332, label: '三级节点 B-3-2' }
        ]
      }
    ]
  }
])

// 搜索相关
const searchText = ref('')
const treeRef = ref(null)
const filteredData = ref([...originalData.value])
const expandedKeys = ref([]) // 用于存储需要展开的节点ID

// 处理搜索
const handleSearch = (value) => {
  if (!value) {
    // 如果搜索为空，恢复原始数据
    filteredData.value = [...originalData.value]
    expandedKeys.value = [] // 清空展开的节点
    return
  }

  // 收集需要展开的节点ID
  const expandedIds = []

  // 过滤数据，保留匹配的节点及其所有子节点
  const filterNodes = (nodes, searchValue, parentMatched = false, parentKey = null) => {
    return nodes.reduce((acc, node) => {
      // 检查当前节点是否匹配搜索值
      const isMatch = parentMatched || node.label.toLowerCase().includes(searchValue.toLowerCase())

      // 如果节点匹配，将其ID添加到展开列表
      if (node.label.toLowerCase().includes(searchValue.toLowerCase())) {
        expandedIds.push(parentKey)
      }

      // 递归处理子节点
      let children = []
      if (node.children && node.children.length) {
        children = filterNodes(node.children, searchValue, isMatch, node.id)
      }

      // 如果当前节点匹配，或者有匹配的子节点，就保留该节点
      if (isMatch || children.length > 0) {
        acc.push({
          ...node,
          children // 这里使用过滤后的子节点
        })
      }
      return acc
    }, [])
  }
  // 执行过滤
  filteredData.value = filterNodes(originalData.value, value)

  // 设置需要展开的节点
  expandedKeys.value = [...expandedIds]
}

// 高亮显示匹配的文本
const highlightMatch = (label) => {
  if (!searchText.value) return label

  const reg = new RegExp(`(${searchText.value})`, 'gi')
  return label.replace(reg, '<span class="highlight">$1</span>')
}

// 监听搜索文本变化
watch(searchText, (newVal) => {
  requestAnimationFrame(() => handleSearch(newVal))
})
</script>

<style scoped>
.tree-search-container {
  max-width: 600px;
  margin: 20px auto;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.search-input {
  margin-bottom: 20px;
}

.custom-tree {
  --el-tree-node-height: 36px;
}

.highlight {
  color: #409eff;
  font-weight: bold;
}
</style>
